use super::MhSha1BlockFunction;
use super::ISAL_HASH_SEGS;
use super::ISAL_MH_SHA1_BLOCK_SIZE;
use super::ISAL_SHA1_BLOCK_SIZE;
use super::ISAL_SHA1_DIGEST_WORDS;

const K_00_19:u32 = 0x5a827999;
const K_20_39:u32 = 0x6ed9eba1;
const K_40_59:u32 = 0x8f1bbcdc;
const K_60_79:u32 = 0xca62c1d6;

macro_rules! F1 { ($b:expr, $c:expr, $d:expr) => { ($d ^ ($b & ($c ^ $d)))};}
macro_rules! F2 { ($b:expr, $c:expr, $d:expr) => { ($b ^ $c ^ $d)};}
macro_rules! F3 { ($b:expr, $c:expr, $d:expr) => { (($b & $c) | ($d & ($b | $c)))};}
macro_rules! F4 { ($b:expr, $c:expr, $d:expr) => { ($b ^ $c ^ $d) };}

macro_rules! update_w {
    ($s:expr, $i:expr, $w:expr) => {
        $w[($i & 15)][$s] = (
            $w[($i - 3) & 15][$s] ^
            $w[($i - 8) & 15][$s] ^
            $w[($i - 14) & 15][$s] ^
            $w[($i - 16) & 15][$s]
        ).rotate_left(1);
    };
}
 
macro_rules! store_w {
    ($s:expr, $i:expr, $w:expr, $data:expr) => {
        $w[$i % 16][$s] = u32::from_be_bytes($data[($i * ISAL_HASH_SEGS + $s)*std::mem::size_of::<u32>()..($i * ISAL_HASH_SEGS + $s + 1)*std::mem::size_of::<u32>()].try_into().unwrap());
    };
}

macro_rules! update_e {
    (1, $s:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $i:expr, $w:expr) => {
        $e[$s] = $e[$s].wrapping_add($a[$s].rotate_left(5)).wrapping_add(F1!($b[$s], $c[$s], $d[$s])).wrapping_add(K_00_19).wrapping_add($w[($i & 15)][$s]);
    };
    (2, $s:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $i:expr, $w:expr) => {
        $e[$s] = $e[$s].wrapping_add($a[$s].rotate_left(5)).wrapping_add(F2!($b[$s], $c[$s], $d[$s])).wrapping_add(K_20_39).wrapping_add($w[($i & 15)][$s]);
    };
    (3, $s:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $i:expr, $w:expr) => {
        $e[$s] = $e[$s].wrapping_add($a[$s].rotate_left(5)).wrapping_add(F3!($b[$s], $c[$s], $d[$s])).wrapping_add(K_40_59).wrapping_add($w[($i & 15)][$s]);
    };
    (4, $s:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $i:expr, $w:expr) => {
        $e[$s] = $e[$s].wrapping_add($a[$s].rotate_left(5)).wrapping_add(F4!($b[$s], $c[$s], $d[$s])).wrapping_add(K_60_79).wrapping_add($w[($i & 15)][$s]);
    };
}

macro_rules! update_b { ($s:expr,$b:expr) => { $b[$s] = $b[$s].rotate_left(30); }; }

macro_rules! store_16w { ($i:expr, $w:expr, $data:expr) => { seq!(N in 0..16 { store_w!(N, $i, $w, $data); }); }; }
macro_rules! update_16w { ($i:expr, $w:expr) => { seq!(N in 0..16 { update_w!(N, $i, $w); }); }; }
macro_rules! update_16e1 { ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $i:expr, $w:expr) => { seq!(N in 0..16 { update_e!(1, N, $a, $b, $c, $d, $e, $i, $w); }); }; }
macro_rules! update_16e2 { ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $i:expr, $w:expr) => { seq!(N in 0..16 { update_e!(2, N, $a, $b, $c, $d, $e, $i, $w); }); }; }
macro_rules! update_16e3 { ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $i:expr, $w:expr) => { seq!(N in 0..16 { update_e!(3, N, $a, $b, $c, $d, $e, $i, $w); }); }; }
macro_rules! update_16e4 { ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $i:expr, $w:expr) => { seq!(N in 0..16 { update_e!(4, N, $a, $b, $c, $d, $e, $i, $w); }); }; }
macro_rules! update_16b { ($b:expr) => { seq!(N in 0..16 { update_b!(N, $b); }); }; }

#[inline(always)]
fn step00_15(i: usize, a: &mut [u32], b: &mut [u32], c: &mut [u32], d: &mut [u32], e: &mut [u32], w: &mut [[u32; ISAL_HASH_SEGS]; 16], ww: &[u8]) {
    store_16w!(i, w, ww);
    update_16e1!(a, b, c, d, e, i, w);
    update_16b!(b);
}

#[inline(always)]
fn step16_19(i: usize, a: &mut [u32], b: &mut [u32], c: &mut [u32], d: &mut [u32], e: &mut [u32], w: &mut [[u32; ISAL_HASH_SEGS]; 16]) {
    update_16w!(i, w);
    update_16e1!(a, b, c, d, e, i, w);
    update_16b!(b);
}

#[inline(always)]
fn step20_39(i: usize, a: &mut [u32], b: &mut [u32], c: &mut [u32], d: &mut [u32], e: &mut [u32], w: &mut [[u32; ISAL_HASH_SEGS]; 16]) {
    update_16w!(i, w);
    update_16e2!(a, b, c, d, e, i, w);
    update_16b!(b);
}

#[inline(always)]
fn step40_59(i: usize, a: &mut [u32], b: &mut [u32], c: &mut [u32], d: &mut [u32], e: &mut [u32], w: &mut [[u32; ISAL_HASH_SEGS]; 16]) {
    update_16w!(i, w);
    update_16e3!(a, b, c, d, e, i, w);
    update_16b!(b);
}
 
#[inline(always)]
fn step60_79(i: usize, a: &mut [u32], b: &mut [u32], c: &mut [u32], d: &mut [u32], e: &mut [u32], w: &mut [[u32; ISAL_HASH_SEGS]; 16]) {
    update_16w!(i, w);
    update_16e4!(a, b, c, d, e, i, w);
    update_16b!(b);
}


#[inline(always)]
fn get_abcde(n:usize, digests:&[[u32;ISAL_HASH_SEGS];ISAL_SHA1_DIGEST_WORDS]) -> [u32;ISAL_HASH_SEGS]
{
    digests[n]
}

#[inline(always)]
fn add_abcde(xx:&mut [u32], n:usize, digests:&mut [[u32;ISAL_HASH_SEGS];ISAL_SHA1_DIGEST_WORDS])
{
    seq!(I in 0..16 { digests[n][I] = digests[n][I].wrapping_add(xx[I]); });
}

/*
 * API to perform 0-79 steps of the multi-hash algorithm for
 * a single block of data. The caller is responsible for ensuring
 * a full block of data input.
 *
 * Argument:
 *   input  - the pointer to the data
 *   digest - the space to hold the digests for all segments.
 *
 * Return:
 *   N/A
 */

fn mh_sha1_single(
    input:&[u8], 
    digests:&mut [[u32;ISAL_HASH_SEGS];ISAL_SHA1_DIGEST_WORDS], 
    frame_buffer:&mut [[u32;ISAL_HASH_SEGS];ISAL_SHA1_BLOCK_SIZE/std::mem::size_of::<u32>()],
) {
        let mut aa= get_abcde(0, digests);
        let mut bb= get_abcde(1, digests);
        let mut cc= get_abcde(2, digests);
        let mut dd= get_abcde(3, digests);
        let mut ee= get_abcde(4, digests);

        let ww = input;
        let w = frame_buffer;

        step00_15(0, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w, ww);
        step00_15(1, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w, ww);
        step00_15(2, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w, ww);
        step00_15(3, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w, ww);
        step00_15(4, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w, ww);
        step00_15(5, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w, ww);
        step00_15(6, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w, ww);
        step00_15(7, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w, ww);
        step00_15(8, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w, ww);
        step00_15(9, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w, ww);
        step00_15(10, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w, ww);
        step00_15(11, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w, ww);
        step00_15(12, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w, ww);
        step00_15(13, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w, ww);
        step00_15(14, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w, ww);
        step00_15(15, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w, ww);

        step16_19(16, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step16_19(17, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step16_19(18, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step16_19(19, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);

        step20_39(20, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step20_39(21, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step20_39(22, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step20_39(23, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step20_39(24, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);
        step20_39(25, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step20_39(26, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step20_39(27, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step20_39(28, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step20_39(29, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);
        step20_39(30, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step20_39(31, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step20_39(32, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step20_39(33, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step20_39(34, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);
        step20_39(35, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step20_39(36, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step20_39(37, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step20_39(38, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step20_39(39, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);

        step40_59(40, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step40_59(41, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step40_59(42, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step40_59(43, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step40_59(44, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);
        step40_59(45, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step40_59(46, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step40_59(47, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step40_59(48, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step40_59(49, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);
        step40_59(50, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step40_59(51, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step40_59(52, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step40_59(53, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step40_59(54, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);
        step40_59(55, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step40_59(56, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step40_59(57, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step40_59(58, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step40_59(59, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);

        step60_79(60, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step60_79(61, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step60_79(62, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step60_79(63, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step60_79(64, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);
        step60_79(65, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step60_79(66, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step60_79(67, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step60_79(68, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step60_79(69, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);
        step60_79(70, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step60_79(71, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step60_79(72, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step60_79(73, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step60_79(74, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);
        step60_79(75, &mut aa, &mut bb, &mut cc, &mut dd, &mut ee, w);
        step60_79(76, &mut ee, &mut aa, &mut bb, &mut cc, &mut dd, w);
        step60_79(77, &mut dd, &mut ee, &mut aa, &mut bb, &mut cc, w);
        step60_79(78, &mut cc, &mut dd, &mut ee, &mut aa, &mut bb, w);
        step60_79(79, &mut bb, &mut cc, &mut dd, &mut ee, &mut aa, w);

        add_abcde(&mut aa, 0, digests);
        add_abcde(&mut bb, 1, digests);
        add_abcde(&mut cc, 2, digests);
        add_abcde(&mut dd, 3, digests);
        add_abcde(&mut ee, 4, digests);
}

#[derive(Clone, Debug, Default)]
pub struct MhSha1BaseWordFrameBuffer([[u32;ISAL_HASH_SEGS];ISAL_SHA1_BLOCK_SIZE/std::mem::size_of::<u32>()]);


#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
pub struct MhSha1Base;

impl MhSha1BlockFunction for MhSha1Base {
    type FrameBuffer = MhSha1BaseWordFrameBuffer;
    fn mh_sha1_block_function(input_data: &[u8], mh_sha1_segs_digests: &mut [[u32;ISAL_HASH_SEGS];ISAL_SHA1_DIGEST_WORDS], aligned_frame_buffer: &mut Self::FrameBuffer, num_blocks: u32) {
        let mut input_data = input_data;
        for _ in 0..num_blocks {
                mh_sha1_single(input_data, mh_sha1_segs_digests, &mut aligned_frame_buffer.0);
                input_data = &input_data[ISAL_MH_SHA1_BLOCK_SIZE..];
        }
        return;
    }
}